<?php


namespace helper\calc;


class Scanner
{
  //operatoren          nummern               wörter                  leerzeichen
  const PATTERN = '/^([!,\+\-\*\/\^%\(\)]|\d*\.\d+|\d+\.\d*|\d+|[a-z_A-Zπ]+[a-z_A-Z0-9]*|[ \t]+)/';

  const ERR_EMPTY = '空循环或者无限循环: `%s`',
      ERR_MATCH = '语法错误: `%s`';

  protected $tokens = array( 0 );

  protected $lookup = array(
      '+' => 65,
      '-' => 66,
      '/' => 68,
      '%' => 69,
      '^' => 70,
      '*' => 67,
      '(' => 8,
      ')' => 16,
      '!' => 73,
      ',' => 32
  );

  public function __construct($input)
  {
    $prev = new Token(64, 'noop');

    while (trim($input) !== '') {
      if (!preg_match(self::PATTERN, $input, $match)) {
        // syntax fehler
        throw new \Exception(sprintf(self::ERR_MATCH, substr($input, 0, 10)));
      }

      if (empty($match[1]) && $match[1] !== '0') {
        // leerer fund -> endlosschleife vermeiden
        throw new \Exception(sprintf(self::ERR_EMPTY, substr($input, 0, 10)));
      }

      // aktuellen wert von input abziehen
      $input = substr($input, strlen($match[1]));

      if (($value = trim($match[1])) === '') {
        // leerzeichen ignorieren
        continue;
      }

      if (is_numeric($value)) {
        if ($prev->type === 16)
          $this->tokens[] = new Token(67, '*');

        $this->tokens[] = $prev = new Token(1, (float) $value);
        continue;
      }

      switch ($type = isset($this->lookup[$value]) ? $this->lookup[$value] : 2) {
        case 65:
          if ($prev->type & 64 || $prev->type == 8) $type = 71;
          break;

        case 66:
          if ($prev->type & 64 || $prev->type == 8) $type = 72;
          break;

        case 8:
          switch ($prev->type) {
            case 2:
              $prev->type = 4;
              break;

            case 1:
            case 16:
              // erlaubt 2(2) -> 2 * 2 | (2)(2) -> 2 * 2
              $this->tokens[] = new Token(67, '*');
              break;
          }

          break;
      }

      $this->tokens[] = $prev = new Token($type, $value);
    }
  }

  public function curr() { return current($this->tokens); }
  public function next() { return next($this->tokens); }
  public function prev() { return prev($this->tokens); }
  public function dump() { print_r($this->tokens); }

  public function peek()
  {
    $v = next($this->tokens);
    prev($this->tokens);

    return $v;
  }
}